package com.example.demo.controller;

import com.example.demo.common.*;
import com.example.demo.component.LoginInterceptor;
import com.example.demo.entity.UserInfo;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.availability.ApplicationAvailability;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;

import static com.example.demo.common.ApplicationVariable.*;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: DELL
 * Date: 2023-06-06
 * Time: 17:19
 */
@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserService userService;

    @Autowired
    private CheckCodeTools codeTools;

    @Autowired
    private TimerTaskTools taskTools;

    @Autowired
    private IpFilter ipFilter;
    /**
     * 由于前端传来的字段和实体类字段对应, 框架自动进行映射赋值
     *
     * @param userInfo 传入对象利于后期维护
     * @return 返回统一对象
     */
//    @RequestMapping("/reg")
//    public AjaxResult reg(UserInfo userInfo) {
//        /**
//         * hasLength -- 判断是否为 null 以及长度是否为空
//         */
//        // 1. 数据校验
//        if (userInfo == null || !StringUtils.hasLength(userInfo.getUsername()))
//            return AjaxResult.fail(-1, "参数有误!");
//
//        // 2. 调用 UserService 中查询 username 方法
//        UserInfo resultUsername = userService.selectByUsername(userInfo.getUsername());
//        if (resultUsername != null)
//            return AjaxResult.fail(-1, "用户已存在!");
//
//        // 传入的密码进行加盐加密得到盐值和加盐加密后的字符串
//        userInfo.setPassword(PasswordTools.encrypt(userInfo.getPassword()));
//
//        // 3. 调用 UserService 执行添加方法
//        int resultUser = userService.reg(userInfo);
//
//        // 4. 设置 data code 返回统一的 AjaxResult
//        return AjaxResult.success(resultUser);
//    }

    /**
     * 用户发送邮件验证码功能
     *
     * @param email    用户邮箱
     * @param username 用户名
     */
//    @RequestMapping("/email")
//    public AjaxResult getEmail(String email, String username, HttpServletRequest request) {
//
//        String regex = "/^[a-zA-Z0-9_.-]+@[a-zA-Z0-9-]+(\\.[a-zA-Z0-9-]+)*\\.[a-zA-Z0-9]{2,6}$/";
//
//        // 1. 生成随机数
//        Random random = new Random();
//        int SendCaptcha = random.nextInt(10_00000 - 100000) + 100000; // 生成一个 六位数的随机验证码
//        System.out.println(SendCaptcha);
//
////        if(SendCaptcha == -1) {
////            AjaxResult.fail(-1, "验证码超时, 请重新发送");
////        }
//        System.out.println(SendCaptcha);
//        // 2. 邮箱检验
//        if (email.matches(regex)) return AjaxResult.fail(-1, "邮箱错误, 请检查邮箱!");
//
//        // 3. 判断邮箱
//        if (email.contains("@qq.com")) {
//            EmailTools.sendMail(email, "Hi ~ " + username + " , 感谢使用博客系统 ! 您的验证码为 : " + SendCaptcha + " 请妥善保管, 请勿轻易告知他人, 验证码有效时长为 5 分钟", "欢迎注册博客~ ");
//        }
//
//        if (email.contains("@163.com")) {
//            EmailTools.sendMail1(email, "Hi ~ " + username + " , 感谢使用博客系统 ! 您的验证码为 : " + SendCaptcha + " 请妥善保管, 请勿轻易告知他人, 验证码有效时长为 5 分钟", "欢迎注册博客~ ");
//        }
//
//        // 建立会话保存验证码
//        HttpSession session = request.getSession();
//        session.setAttribute(ApplicationVariable.SEND_CAPTCHA, SendCaptcha);
//
//        return AjaxResult.success(1); // 1 为正常发送邮箱
//    }

    /**
     * 用户注册时发送邮箱验证码功能, 同时开启邮箱验证码过期定时任务
     *
     * @param email    用户邮箱
     * @param username 用户名
     * @param request  用户请求
     * @return
     */
    // 记录验证码和创建时间的Map
    @RequestMapping("/email")
    public AjaxResult sendEmail(String email, String username, HttpServletRequest request) {

        String regex = "/^[a-zA-Z0-9_.-]+@[a-zA-Z0-9-]+(\\.[a-zA-Z0-9-]+)*\\.[a-zA-Z0-9]{2,6}$/";

        // 1. 生成随机数
        Random random = new Random();
        int SendCaptcha = random.nextInt(10_00000 - 100000) + 100000; // 生成一个 六位数的随机验证码

        // 2. 邮箱检验
        if (email.matches(regex)) return AjaxResult.fail(-1, "邮箱错误, 请检查邮箱!");

        // 3. 判断邮箱
        if (email.contains("@qq.com")) {
            EmailTools.sendMail(email, "Hi ~ " + username + " , 感谢使用博客系统 ! 您的验证码为 : " + SendCaptcha + " 请妥善保管, 请勿轻易告知他人, 验证码有效时长为 3 分钟", "欢迎注册博客~ ");
        }

        if (email.contains("@163.com")) {
            EmailTools.sendMail1(email, "Hi ~ " + username + " , 感谢使用博客系统 ! 您的验证码为 : " + SendCaptcha + " 请妥善保管, 请勿轻易告知他人, 验证码有效时长为 3 分钟", "欢迎注册博客~ ");
        }

//        // 4. 将验证码和当前的发送时间记录到 map 中
//        codeTimeMap.put(SendCaptcha, System.currentTimeMillis());
        // 将验证码存入
//        System.out.println("发送邮件后存入验证码" + SendCaptcha);

        // 将验证码保存如验证码工具类中进行过期检验
        taskTools.saveCode(SendCaptcha);

        // 建立会话保存验证码
        HttpSession session = request.getSession();
        session.setAttribute(ApplicationVariable.SEND_CAPTCHA, SendCaptcha);
        //System.out.println("查看后端生成验证码是否存入 Session 中 " + session.getAttribute(ApplicationVariable.SEND_CAPTCHA));
        return AjaxResult.success(1); // 1 为正常发送邮箱
    }


//    public void checkCodeExpired() {
//        Timer timer = new Timer();
//        timer.schedule(new TimerTask() {
//            @Override
//            public void run() {
//                long currentTimeMillis = System.currentTimeMillis();
//                for (Integer code : codeTimeMap.keySet()) {
//                    long createTimeMillis = codeTimeMap.get(code);
//                    if (currentTimeMillis - createTimeMillis > 60000) { // 180000 毫秒即3分钟
//                        // 如果验证码已经过期，则将其标记为失效
//                        codeTimeMap.remove(code);
//                    }
//                }
//            }
//        }, 1000, 1000); // 每过一秒执行一次
//    }

    /**
     * 注册功能, 并从验证码工具类获取验证码检验是否有效时间内
     */
    @RequestMapping("/reg2")
    @Transactional
    public AjaxResult reg2(UserInfo userInfo, HttpServletRequest request) {

        HttpSession session = request.getSession(false);
        if (session == null || session.getAttribute(ApplicationVariable.SEND_CAPTCHA) == null)
            return AjaxResult.fail(-1, "参数异常");
        // 最先需要判断验证码是否正确
        // 从会话中获取验证码
        Integer code = (int) session.getAttribute(ApplicationVariable.SEND_CAPTCHA);

//        // 拿取时间
//        Long createTimeMillis = codeTimeMap.get(code);
//        if( createTimeMillis == null || System.currentTimeMillis() - createTimeMillis > 60000) {
//            // 验证码过期
//            return AjaxResult.fail(-1, "验证码已失效, 请重新发送");
//        }

        //System.out.println(taskTools.getCodeTimeMap());
        // 检验验证码是否过期
        boolean isExpired = !taskTools.getCodeTimeMap().containsKey(code); // true - 没过期
        //System.out.println(isExpired);
        if (isExpired) {
            return AjaxResult.fail(-1, "验证码已失效, 请重新发送");
        }


        // 1. 非空数据效验
        if (userInfo == null || !StringUtils.hasLength(userInfo.getUsername()) ||
                !StringUtils.hasLength(userInfo.getPassword())) {
            return AjaxResult.fail(-1, "参数异常");
        }

        // 获取之前发送后存储的验证码并和前端传入的验证码比对
        if (!session.getAttribute(ApplicationVariable.SEND_CAPTCHA).equals(userInfo.getCaptcha()))
            return AjaxResult.fail(-1, "验证码错误!");


        // 2. 调用 UserService 中查询 username 方法
        UserInfo resultUsername = userService.selectByUsername(userInfo.getUsername());
        if (resultUsername != null)
            return AjaxResult.fail(-1, "用户已存在!");

        // 3. 传入的密码进行加盐加密得到盐值和加盐加密后的字符串
        userInfo.setPassword(PasswordTools.encrypt(userInfo.getPassword()));

        int result = userService.reg2(userInfo);
        return AjaxResult.success(result);

    }

    /**
     * 前端请求获取图形验证码功能, 并通配置文件映射到 Linux 服务器 /root/image/ 下
     */
    @RequestMapping("/getcode")
    public AjaxResult getCode(HttpServletRequest request) {
        String[] codeArr = codeTools.createImage();
        // 将验证码存储到 session
        HttpSession session = request.getSession();
        session.setAttribute(SESSION_KEY_CHECK_CODE, codeArr[1]); // 图形验证码
        return AjaxResult.success("/image/" + codeArr[0]);
    }


//    @RequestMapping("/login")
//    public AjaxResult login(UserInfo userInfo, HttpServletRequest request) {
//        // 1. 非空效验
//        if (userInfo == null || !StringUtils.hasLength(userInfo.getUsername()))
//            return AjaxResult.fail(-1, "参数有误!");
//
//        // 2. 根据用户名查询用户
//        UserInfo userInfo1 = userService.selectByUsername(userInfo.getUsername());
//        if (userInfo1 == null) {
//            // 用户名不存在
//            return AjaxResult.fail(-1, "用户名或密码错误");
//        }
//
//        // 3. 根据查询到的用户, 获取其密码和前端传来的密码对比
////        if (!userInfo1.getPassword().equals(userInfo.getPassword()))
////            return AjaxResult.fail(-1, " 用户名或密码错误");
//
//        if (!PasswordTools.decrypt(userInfo.getPassword(), userInfo1.getPassword())) {
//            return AjaxResult.fail(-1, "用户名或密码错误!");
//        }
//
//        // 4. 建立会话信息存储用户
//        HttpSession session = request.getSession();
//        session.setAttribute(ApplicationVariable.SESSION_USERINFO_KEY, userInfo1);
//
//        return AjaxResult.success(1); // 设置为 1 表示登陆正确  0 表示登陆失败
//
//    }


    /**
     * 判断当前是否为登陆状态
     */
    @RequestMapping("/islogin")
    public AjaxResult isLogin(HttpServletRequest request) {
        UserInfo userInfo = GetUserSessionTools.getLoginUser(request);
        if (userInfo == null) return AjaxResult.fail(-1, "当前用户未登录");
        return AjaxResult.success(1);
    }


    public static final Map<String, Long> BLACKLIST_MAP = new ConcurrentHashMap<>(); // 用于存储 ip 地址和设定的拉黑时间十分钟
    private static final Map<String, Integer> LOGIN_ERROR_MAP = new ConcurrentHashMap<>();// 用于存储用户名和当前错误次数

    /**
     * 用户登录功能, 登陆错误时记录当期 ip 并设置其对应的错误次数, 当超过三次时, 加入黑名单禁止登录十分钟
     *
     * @param username   用户名
     * @param password   明文密码
     * @param code_input 输入的验证码
     * @return
     */
    @RequestMapping("/login")
    public AjaxResult login(String username, String password, String code_input, HttpServletRequest request) {

        // 先行检查是否在黑名单中, 存在则不执行后续登陆流程
//        if (BLACKLIST_MAP.containsKey(request.getRemoteAddr())) {
//            // 当前存在黑名单中
//            long blackTime = BLACKLIST_MAP.get(request.getRemoteAddr());
//            // 如果当前时间小于黑名单中 ip 对应的过期时间, 说明还未拉出黑名单
//            if (System.currentTimeMillis() < blackTime) {
//                return AjaxResult.fail(-1, "多次错误登陆导致账号异常,请联系管理员或十分钟后再尝试!");
//            } else {
//                BLACKLIST_MAP.remove(request.getRemoteAddr()); // 拉出黑名单
//            }
//
//        }

        // 1. 非空效验
        if (!StringUtils.hasLength(username) || !StringUtils.hasLength(password)) {
            return AjaxResult.fail(-1, "参数异常");
        }

//        // 2.验证码校验
//        if (!code_input.equals(request.getSession(false).getAttribute(SESSION_KEY_CHECK_CODE))) {
//            return AjaxResult.fail(-1, "验证码错误");
//        }

        // 3. 密码判断
        UserInfo userInfo = userService.login(username);
        if (userInfo == null || userInfo.getId() <= 0) {
            // 登录失败，记录当前账户对应的错误次数
            int errorCount = LOGIN_ERROR_MAP.getOrDefault(username, 0); //
            errorCount++;
            LOGIN_ERROR_MAP.put(username, errorCount); // 存入当前用户和错误次数

            // 当错误次数超过三次
//            if (errorCount == 3) {
//                // 拉黑当前用户 ip 并把当前用户的拉黑时间设为 10 分钟
//                BLACKLIST_MAP.put(request.getRemoteAddr(), System.currentTimeMillis() + 10 * 60 * 1000); // 加入黑名单
//            }

            return AjaxResult.fail(-1, "用户名或密码错误");
        }

        if (!PasswordTools.decrypt(password, userInfo.getPassword())) {
            // 密码错误，记录错误次数并加入黑名单
//            int errorCount = LOGIN_ERROR_MAP.getOrDefault(username, 0);
//            errorCount++;
//
//            LOGIN_ERROR_MAP.put(username, errorCount);// 存入当前用户和错误次数
//
//            // 当错误次数超过三次
//            if (errorCount == 3) {
//                // 拉黑当前用户 ip 并把当前用户的拉黑时间设为 10 分钟
//                BLACKLIST_MAP.put(request.getRemoteAddr(), System.currentTimeMillis() + 10 * 60 * 1000); // 加入黑名单
//            }
            return AjaxResult.fail(-1, "用户名或密码错误");
        }

        // 登陆成功, 清空当前用户的错误计数器
        //LOGIN_ERROR_MAP.remove(username);

        // 4. 建立会话信息存储用户
        HttpSession session = request.getSession(false);
        session.setAttribute(ApplicationVariable.SESSION_USERINFO_KEY, userInfo);
        // 用户登录成功, 重置当前 ip 的 count

        return AjaxResult.success(1);
    }

    /**
     * 重置密码功能-找回密码
     */
    @RequestMapping("/forgot")
    @Transactional
    public AjaxResult forgot(String username, String password, String email, Integer code_input, HttpServletRequest request) {
        HttpSession session = request.getSession();

        if (session == null || session.getAttribute(ApplicationVariable.SEND_CAPTCHA) == null)
            return AjaxResult.fail(-1, "参数异常");

        // 1. 非空效验
        if (!StringUtils.hasLength(username) || !StringUtils.hasLength(email)) {
            return AjaxResult.fail(-1, "参数异常");
        }

        // 验证码失效检验
        Integer code = (int) session.getAttribute(ApplicationVariable.SEND_CAPTCHA);
        boolean isExpired = !taskTools.getCodeTimeMap().containsKey(code); // 不取反为 true - 没过期

        if (isExpired) {
            return AjaxResult.fail(-1, "验证码已失效, 请重新发送");
        }

//        System.out.println("前端用户传入验证码" + code_input);
//        System.out.println("后端获取存储的验证码" + session.getAttribute(ApplicationVariable.SEND_CAPTCHA));

        // 2. 验证码校验
        if (!code_input.equals(session.getAttribute(ApplicationVariable.SEND_CAPTCHA))) {
            return AjaxResult.fail(-1, "验证码错误");
        }

        // 3. 根据用户名和邮箱查询用户是否存在
        UserInfo userInfo = userService.selectByUsernameAndEmail(username, email);

        // 4. 将新密码加盐加密处理
        // PasswordTools.encrypt(password);

        // 5. 根据用户名和邮箱找到指定用户修改新密码
        int result = userService.updatePassword(username, email, PasswordTools.encrypt(password));

        return AjaxResult.success(1);
    }

    /**
     * 注销功能
     */
    @RequestMapping("/exit")
    public AjaxResult exit(HttpServletRequest request) {
        HttpSession session = request.getSession(false);
        session.removeAttribute(ApplicationVariable.SESSION_USERINFO_KEY); // 移除该 Session
        return AjaxResult.success(1);
    }

    /**
     * 用户上传文件功能-目前仅限于 img jpg gif pmg 图片
     *
     * @param file 用户上传的文件
     * @return
     */
    @RequestMapping("/upload")
    @Transactional
    public AjaxResult upload(MultipartFile file, HttpServletRequest request) {
        UserInfo userInfo = GetUserSessionTools.getLoginUser(request);

        if (userInfo == null) return AjaxResult.fail(-1, "参数异常");

        // 1. 文件效验
        if (file == null || file.isEmpty()) {
            return AjaxResult.fail(-1, "文件异常");
        }

        // 2. 创建文件名名称
        String fileName = UUID.randomUUID() + //文件名
                file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf(".")); // 后缀名
        // lastIndexOf 左闭右开需要注意, 包含分割后的 . 的

        // Linux 上对应相对路径为 /root/image/pric/
        File saveFile = new File("E:\\Javacode\\image\\pric\\" + fileName); // 保存文件
        try {
            file.transferTo(saveFile); // 上传文件

            String dbFile = "/image/pric/" + fileName;
            int result = userService.filePath(dbFile, userInfo.getId());
            if (result != 1) return AjaxResult.fail(-1, "存储图片错误");
            // 3. 将图片路径存储到数据库中
            return AjaxResult.success(dbFile);

            // return AjaxResult.success(saveFile);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return AjaxResult.fail(-1, "参数异常");
    }

    /**
     * 初始化图片功能, 加载个人中心头像
     *
     * @return
     */
    @RequestMapping("/initpric")
    public AjaxResult initpric(HttpServletRequest request) {
        UserInfo userInfo = GetUserSessionTools.getLoginUser(request);
        if (userInfo == null) return AjaxResult.fail(-1, "当前状态异常");

        UserInfo userInfo1 = userService.getImagePath(userInfo.getId());
        return AjaxResult.success(userInfo1);
    }

    /**
     * 个人中心修改密码功能
     *
     * @param old_password 旧密码
     * @param new_password 新密码
     * @return
     */
    @RequestMapping("/revisepassword")
    @Transactional
    public AjaxResult revisePassword(String old_password, String new_password, HttpServletRequest request) {
        // 1. 数据效验
        if (!StringUtils.hasLength(old_password) || !StringUtils.hasLength(new_password)) {
            return AjaxResult.fail(-1, "参数异常");
        }
        // 2. 解密数据库中的密码
        UserInfo userInfo = GetUserSessionTools.getLoginUser(request);
        if (userInfo == null) return AjaxResult.fail(-1, "当前状态异常");
        // 2.1 解密
        boolean flag = PasswordTools.decrypt(old_password, userInfo.getPassword());
        if (!flag) return AjaxResult.fail(-1, "旧密码错误, 请重新输入");

        // 3. 新密码加盐后进行修改
        int result = userService.revisePassword(userInfo.getId(), PasswordTools.encrypt(new_password));
        return AjaxResult.success(result);
    }
}
